/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * This file is part of retabel.
 *
 * Copyright (c) 2011 by individual contributors as indicated by the
 * @author tags. See the copyright.txt file in the distribution
 * for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.retabel.web.rest;

import org.apache.commons.lang.StringUtils;
import org.retabel.persistence.entity.render.RenderedDocument;
import org.retabel.persistence.entity.render.RenderedDocumentPart;
import org.retabel.service.manager.RenderedDocumentManager;
import xsd.rendereddocument.RenderedDocumentPartList;
import xsd.rendereddocuments.ObjectFactory;
import xsd.rendereddocuments.RenderedDocumentList;
import xsd.rendereddocuments.RenderedDocuments;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import java.util.GregorianCalendar;

/**
 * Config set REST interface implementation.
 * <p/>
 * <h3>Extra-Info</h3>
 * Created: 10:19 16.07.11
 *
 * @author Peter Herber
 * @version 1.0
 */
@Path("/rendereddocuments/")
@Stateless
public class RenderedDocumentServiceBean extends RestServiceBean {


    @EJB
    private RenderedDocumentManager renderedDocumentManager;


    /**
     * Retrieve a ranged list of available rendered documents.
     *
     * @param first   the index of the first rendered document to retrieve.
     * @param maxRows the maximum number of matches to retrieve.
     * @return xml containing the list of rendered documents matching the given search criteria.
     * @throws javax.xml.datatype.DatatypeConfigurationException if an error occurs.
     */
    @GET
    @Produces(MediaType.TEXT_XML)
    public Response getRenderedDocuments(@QueryParam("first") Integer first,
                                         @QueryParam("maxRows") Integer maxRows)
        throws DatatypeConfigurationException {
        int maxRowsWithFallback = maxRows != null ? maxRows : getDefaultMaxResultsPerPage();
        int firstWithFallback = first != null ? first : 0;

        int count = renderedDocumentManager.count();
        RenderedDocument[] renderedDocuments = renderedDocumentManager.findRange(firstWithFallback, maxRowsWithFallback);

        ObjectFactory of = new ObjectFactory();
        xsd.rendereddocument.ObjectFactory ofRenderedDocument = new xsd.rendereddocument.ObjectFactory();

        RenderedDocuments result = of.createRenderedDocuments();
        result.setStart(firstWithFallback);
        result.setRows(renderedDocuments.length);
        result.setCount(count);

        RenderedDocumentList renderedDocumentList = of.createRenderedDocumentList();
        result.setList(renderedDocumentList);

        for (RenderedDocument renderedDocument : renderedDocuments) {
            xsd.rendereddocument.RenderedDocument xmlRenderedDocument = ofRenderedDocument.createRenderedDocument();
            xmlRenderedDocument.setDocumentIdentifier(renderedDocument.getDocumentIdentifier());

            GregorianCalendar calendar = new GregorianCalendar();
            calendar.setTime(renderedDocument.getCreated());
            XMLGregorianCalendar xmlCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar);

            xmlRenderedDocument.setCreated(xmlCalendar);

            RenderedDocumentPartList renderedDocumentPartList = ofRenderedDocument.createRenderedDocumentPartList();
            xmlRenderedDocument.setList(renderedDocumentPartList);

            for (RenderedDocumentPart renderedDocumentPart : renderedDocument.getRenderedDocumentParts()) {
                xsd.rendereddocument.RenderedDocumentPart xmlRenderedDocumentPart = ofRenderedDocument.createRenderedDocumentPart();
                xmlRenderedDocumentPart.setMimeType(renderedDocumentPart.getContentType().getMimeType());
                xmlRenderedDocumentPart.setId(renderedDocumentPart.getId());
                xmlRenderedDocumentPart.setFilename(getRenderedDocumentPartFilename(renderedDocument.getDocumentIdentifier(),
                                                                                    renderedDocumentPart));

                renderedDocumentPartList.getRenderedDocumentPart().add(xmlRenderedDocumentPart);
            }

            renderedDocumentList.getRenderedDocument().add(xmlRenderedDocument);
        }

        return Response.ok(result).build();
    }

    /**
     * Retrieve the rendered document description by document identifier
     *
     * @param documentIdentifier     the document identifier of the rendered document to retrieve.
     * @return xml containing the the rendered document description
     *
     * @throws javax.xml.datatype.DatatypeConfigurationException if an error occurs.
     */
    @GET
    @Path("/{documentIdentifier}")
    @Produces(MediaType.TEXT_XML)
    public Response getRenderedDocument(@PathParam("documentIdentifier") String documentIdentifier)
        throws DatatypeConfigurationException {
        RenderedDocument renderedDocument = renderedDocumentManager.findByDocumentIdentifier(documentIdentifier);

        if (renderedDocument == null) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }

        xsd.rendereddocument.ObjectFactory of = new xsd.rendereddocument.ObjectFactory();

        xsd.rendereddocument.RenderedDocument result = of.createRenderedDocument();
        result.setDocumentIdentifier(renderedDocument.getDocumentIdentifier());

        GregorianCalendar calendar = new GregorianCalendar();
        calendar.setTime(renderedDocument.getCreated());
        XMLGregorianCalendar xmlCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(calendar);

        result.setCreated(xmlCalendar);

        RenderedDocumentPartList renderedDocumentPartList = of.createRenderedDocumentPartList();
        result.setList(renderedDocumentPartList);

        for (RenderedDocumentPart renderedDocumentPart : renderedDocument.getRenderedDocumentParts()) {
            xsd.rendereddocument.RenderedDocumentPart xmlRenderedDocumentPart = of.createRenderedDocumentPart();
            xmlRenderedDocumentPart.setMimeType(renderedDocumentPart.getContentType().getMimeType());
            xmlRenderedDocumentPart.setId(renderedDocumentPart.getId());
            xmlRenderedDocumentPart.setFilename(getRenderedDocumentPartFilename(renderedDocument.getDocumentIdentifier(),
                                                                                renderedDocumentPart));

            renderedDocumentPartList.getRenderedDocumentPart().add(xmlRenderedDocumentPart);
        }

        return Response.ok(result).build();
    }

    /**
     * Retrieve the rendered document part by document identifier and rendered document part id.
     *
     * @param documentIdentifier     the document identifier of the rendered document to retrieve.
     * @param renderedDocumentPartId the rendered document part id of the rendered document part to retrieve.
     * @return the rendered document part as byte stream.
     */
    @GET
    @Path("/{documentIdentifier}/{renderedDocumentPartId}")
    public Response getRenderedDocumentPart(@PathParam("documentIdentifier") String documentIdentifier,
                                            @PathParam("renderedDocumentPartId") Integer renderedDocumentPartId) {
        RenderedDocument renderedDocument = renderedDocumentManager.findByDocumentIdentifier(documentIdentifier);

        if (renderedDocument == null) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }

        RenderedDocumentPart resultRenderedDocumentPart = null;

        for (RenderedDocumentPart renderedDocumentPart : renderedDocument.getRenderedDocumentParts()) {
            if (renderedDocumentPart.getId() == renderedDocumentPartId.intValue()) {
                resultRenderedDocumentPart = renderedDocumentPart;
                break;
            }
        }

        if (resultRenderedDocumentPart == null) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }

        String filename = getRenderedDocumentPartFilename(documentIdentifier, resultRenderedDocumentPart);

        return Response.ok().header("Content-type", resultRenderedDocumentPart.getContentType().getMimeType()).header(
            "Content-Disposition", "inline; filename=\"" + filename + "\"").entity(resultRenderedDocumentPart.getContent())
                       .build();
    }

    /**
     * Delete an rendered document by it's document identifier.
     *
     * @param documentIdentifier the unique document identifier.
     * @param uriInfo            the uri info context used for logging purposes.
     * @return plain/text log lines.
     */
    @DELETE
    @Path("/{documentIdentifier}")
    @Produces(MediaType.TEXT_PLAIN)
    public Response deleteRenderedDocument(@PathParam("documentIdentifier") String documentIdentifier, @Context UriInfo uriInfo) {
        PlainTextResponseBuilder response = new PlainTextResponseBuilder(uriInfo);

        response.println("Deleting rendered document: " + documentIdentifier);

        renderedDocumentManager.remove(documentIdentifier);

        return response.getResponse();
    }

    /**
     * Builds the rendered document part filename based.
     *
     * @param documentIdentifier   the rendered document identifier.
     * @param renderedDocumentPart the rendered document part.
     * @return the filename of the rendered document part.
     */
    private String getRenderedDocumentPartFilename(String documentIdentifier, RenderedDocumentPart renderedDocumentPart) {
        return StringUtils.join(new Object[]{documentIdentifier, "_", renderedDocumentPart.getId(),
                                             renderedDocumentPart.getContentType().getFilenameExtension()});
    }

}
